import os, shutil
from settings import FILE_DB_ROOT
from django.template import Context, Template
from django.core import serializers
from django.core.exceptions import ValidationError

class SimpleFileDb(object):
    ROOT = FILE_DB_ROOT
    FILE, DIR = range(2)
    
    # If the file type is a directory
    # then a file will be created in
    # the directory with additional info
    DIR_INFO_FILE = "00_info.xml"
    
    def __init__(self, path_type, path=None, context={}):
        """
        path can be a string or list
            if a list then os.path.join will be used to join the elements
        
        path_type can be 0 (FILE) or 1 (DIR)
        
        context is a dictionary that will be used to subtitute template variables ({{ REPLACE_X }}) that exist in path or root args
        """
        self.context = Context(context)
        self.setPath(path)
        self.is_saved = False
        if path_type in [self.FILE, self.DIR]:
            self.path_type = path_type
        else:
            raise ValueError("path_type can only be %i (FILE) or %i (DIR)" % (self.FILE, self.DIR))
    
    def _renderPath(self, path):
        if isinstance(path, str) or isinstance(path, unicode):
            path = [path]
        path = os.path.join(*path)
        path = Template(path).render(self.context)
        # TODO: sanitize path
        path = path.strip().replace(" ", "_")  # TODO: replace with regex to skip '\ '
        return path
    
    def setPath(self, path=None):
        if path is not None:
            path = self._renderPath(path)
        self.path = path
        self.orig_path = self.path
        self.is_checked = False
        self.is_path_changed = False
        self.allow_save = False
        return self.path
    
    def updatePath(self, path):
        path = self._renderPath(path)
        if path != self.path:   # path is not self.path doesn't work here
            self.is_checked = False
            self.is_path_changed = True
            self.orig_path = self.path
            self.path = path
        return self.path
        
    def _getFullPath(self, path):
        return os.path.join(*[self.ROOT, path])
    
    @property
    def fullpath(self):
        return self._getFullPath(self.path)
    
    @property
    def is_new(self):
        """
        Returns True is new path, otherwise False
        """
        return self.orig_path is None or self.orig_path is ''
    
    @property
    def is_old(self):
        """
        Returns True if old path, otherwise False
        """
        return self.orig_path is not None
    
    def check(self):
        """
        Checks if can create/rename file based db element
        only if path has changed from initialization
        
        Returns fullpath: self.ROOT + self.path
        """
        fullpath = self._getFullPath(self.path)
        
        if self.is_checked:
            return fullpath
        if self.path is None:
            raise ValueError("No path to check (i.e., is None)")
        
        if self.is_path_changed:
            dirpath = os.path.dirname(fullpath)
            if os.path.exists(fullpath) is True and self.path_type is self.DIR:
                raise ValidationError("The path to the directory '%s' already exists" % self.path)
            if os.access(dirpath, os.F_OK or os.R_OK or os.W_OK) is False:
                raise ValidationError("Path does not exist or you do not have read/write permissions for path '%s'" % dirpath)
        
        self.is_checked = True
        return fullpath
    
    def _serialize(self, instance, fullpath):
        """
        Writes out XML file with information in 'instance' to 'fullpath'
        """
        fout = open(fullpath, "w")
        serializers.serialize('xml', [instance], stream=fout)
        fout.close()
    
    def save(self, instance):
        """
        Creates or renames a file/directory
        
        If saving a file, will serialize the instance arg as yaml f
        """
        if self.is_saved:
            return
        
        fullpath = self.check()
        
        if self.path_type is self.DIR:
            if self.is_new:  # new
                os.mkdir(fullpath, 0744)  # todo: check if mode is good
            elif self.is_path_changed:
                orig_fullpath = self._getFullPath(self.orig_path)
                shutil.move(orig_fullpath, fullpath)
            self._serialize(instance, os.path.join(fullpath, self.DIR_INFO_FILE))
        elif self.path_type is self.FILE:
            if self.is_path_changed and self.is_old:
                orig_fullpath = self._getFullPath(self.orig_path)
                os.remove(orig_fullpath)
            self._serialize(instance, fullpath)
        else:
            raise Exception("Unkown path_type: %i" % self.path_type)
        
        self.is_saved = True
    

